From 71363266755035083291e66344fa321a999829f2 Mon Sep 17 00:00:00 2001 From: robertl Date: Fri, 10 Jan 2003 19:58:07 +0000 Subject: [PATCH] Implement Garmin Mapsource format. --- gpsbabel/README | 10 ++ gpsbabel/mapsource.c | 267 ++++++++++++++++++++++++++++++++----------- gpsbabel/testo | 19 ++- gpsbabel/vecs.c | 6 +- 4 files changed, 223 insertions(+), 79 deletions(-) diff --git a/gpsbabel/README b/gpsbabel/README index a0c37019e..0979321b3 100644 --- a/gpsbabel/README +++ b/gpsbabel/README @@ -144,6 +144,16 @@ THE FORMATS creating software like this possible. It reads and writes only waypoints (not routes) at this time. + MAPSOURCE + + Garmin Mapsource format appears compatible with the various + members of that product family. Icon mapping is oriented toward + versions above 4.07. Altitude, proximity, and depth are not + supported. + + Information on the Garmin Mapsource format was provided by Ian + Cowley. The code was implemented by Robert Lipe. + PCX Garmin documents only PCX5, an older format limited to the lame NMEA diff --git a/gpsbabel/mapsource.c b/gpsbabel/mapsource.c index 492238351..16876167a 100644 --- a/gpsbabel/mapsource.c +++ b/gpsbabel/mapsource.c @@ -1,5 +1,6 @@ /* Acess to Garmin MapSource files. + Based on information provided by Ian Cowley. Copyright (C) 2002 Robert Lipe, robertlipe@usa.net @@ -24,111 +25,239 @@ #include "defs.h" #include -static FILE *mapsource_file_in; -static FILE *mapsource_file_out; +static FILE *mps_file_in; +static FILE *mps_file_out; #define MYNAME "MAPSOURCE" +/* + * File header. MsRcd ... Nov_18_2002 14:11:40 + */ +char mps_hdr[] = { + 0x4d, 0x73, 0x52, 0x63, 0x64, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x44, 0x67, 0x00, 0x1b, 0x00, 0x00, + 0x00, 0x41, 0x96, 0x01, 0x53, 0x51, 0x41, 0x00, + 0x4f, 0x63, 0x74, 0x20, 0x32, 0x32, 0x20, 0x32, + 0x30, 0x30, 0x31, 0x00, 0x31, 0x35, 0x3a, 0x34, + 0x35, 0x3a, 0x30, 0x35, 0x00 +}; + +char mps_ftr[] = { + 0x02, 0x00, 0x00, 0x00, 0x56, 0x00, 0x01 +}; + +typedef struct icon_mapping { + const int symnum; + const char *icon; +} icon_mapping_t; + +/* + * This list is meant for Mapsource version 4.07 and newer and is derived + * from the list at http://home.online.no/~sigurdhu/MapSource-text.htm . + * Someone more motivated than myself is encouraged to type the rest of + * these in. + */ +static icon_mapping_t icon_table[] = { + { 0, "Marina" }, + { 6, "Bank" }, + { 6, "ATM" }, + { 11, "Restaraunt" }, + { 18, "Waypoint" }, + { 150, "Boating" }, + { 150, "Boat Ramp" }, + { 151, "Campground" }, + { 151, "Camping" }, + { 159, "Park" }, + { 170, "Car" }, + { -1, NULL }, +}; + +const char * +mps_find_desc_from_icon_number(const int icon) +{ + icon_mapping_t *i; + + for (i = icon_table; i->icon; i++) { + if (icon == i->symnum) { + return i->icon; + } + } + return "Waypoint"; +} + +const int +mps_find_icon_number_from_desc(const char *desc) +{ + icon_mapping_t *i; + int def_icon = 18; + + if (!desc) + return def_icon; + + for (i = icon_table; i->icon; i++) { + if (desc == i->icon) { + return i->symnum; + } + } + return def_icon; +} + static void -mapsource_rd_init(const char *fname, const char *args) +mps_rd_init(const char *fname, const char *args) { - mapsource_file_in = fopen(fname, "r"); - if (mapsource_file_in == NULL) { + mps_file_in = fopen(fname, "rb"); + if (mps_file_in == NULL) { fatal(MYNAME ": '%s' for reading\n", fname); } } static void -mapsource_rd_deinit(void) +mps_rd_deinit(void) { - fclose(mapsource_file_in); + fclose(mps_file_in); } static void -mapsource_wr_init(const char *fname, const char *args) +mps_wr_init(const char *fname, const char *args) { - mapsource_file_out = fopen(fname, "w"); - if (mapsource_file_out == NULL) { + mps_file_out = fopen(fname, "wb"); + if (mps_file_out == NULL) { fatal(MYNAME ": '%s' for writing\n", fname); exit(1); } } static void -mapsource_wr_deinit(void) +mps_wr_deinit(void) { - fclose(mapsource_file_out); + fclose(mps_file_out); } +/* + * get characters until and including terminating NULL from mps_file_in + * and write into buf. + */ static void -mapsource_read(void) +mps_readstr(char *buf, size_t sz) { - char name[9], date[20], timeb[20]; - char ibuf[200]; - double lat,lon; - char latdir, londir; - int latd, lond; - float latf, lonf; - int alt; - char altunits[10]; - char icon[20]; - waypoint *wpt_tmp; - - while (fgets(ibuf, sizeof(ibuf), mapsource_file_in)) { - sscanf(ibuf, - "Waypoint %s %s %s %c%d %f %c%d %f %d %s Symbol & Name %s", - name, date, timeb, &latdir, &latd, &latf, &londir, &lond, &lonf, &alt, altunits, icon); - wpt_tmp = xcalloc(sizeof(*wpt_tmp),1); -/* FIXME: Implement actual appropriate conversion */ - wpt_tmp->position.altitude.altitude_meters = alt * 3.0; - wpt_tmp->shortname = xstrdup(name); - wpt_tmp->description = xstrdup(name); - - lat = latd + latf/100.0; - lon = lond + lonf/100.0; - if (latdir == 'S') lat = -lat; - if (londir == 'W') lon = -lon; - wpt_tmp->position.latitude.degrees = lat; - wpt_tmp->position.longitude.degrees = lon; - - waypt_add(wpt_tmp); + int c; + while (sz-- && (c = fgetc (mps_file_in)) != EOF) { + *buf++ = c; + if (c == 0) { + return; + } } } + static void -mapsource_waypt_pr(const waypoint *waypointp) +mps_read(void) { - char tbuf[1024]; - char *tp = tbuf; - int d; - strftime(tbuf, sizeof(tbuf), "%d-%b-%y %I:%M:%S%p", localtime(&waypointp->creation_time)); - while (*tp) { - *tp = toupper(*tp); - tp++; + char hdr[100]; + char tbuf[100]; + char wptname[256]; + char wptdesc[256]; + int reclen; + int lat; + int lon; + waypoint *wpt; + + fread(hdr, 45, 1, mps_file_in); + for(;;) + { + long next_rec; + char icon; + fread(&reclen, 4, 1, mps_file_in); + reclen = le_read32(&reclen); + fread(tbuf, 1, 1, mps_file_in); /* 'W' */ + /* + * Use this to seek to next record to make us more immune + * to changes in file format. + */ + next_rec = ftell(mps_file_in) + reclen; + /* + * Each record has a 'W'. When we run out of those, it's EOF. + */ + if (tbuf[0] != 'W') + break; + wpt = xcalloc(sizeof(*wpt), 1); + + mps_readstr(wptname, sizeof(wptname)); + fread(tbuf, 9, 1, mps_file_in); /* unknown, 0 */ + fread(tbuf, 12, 1, mps_file_in); /* unknown, 0xff */ + fread(tbuf, 6, 1, mps_file_in); /* unknown 0 0 ff ff ff ff */ + + fread(&lat, 4, 1, mps_file_in); + fread(&lon, 4, 1, mps_file_in); + lat = le_read32(&lat); + lon = le_read32(&lon); + + fread(tbuf, 9, 1, mps_file_in); /* alt, format unknown */ + + mps_readstr(wptdesc, sizeof(wptdesc)); + + fread(tbuf, 17, 1, mps_file_in); /* unknown */ + fread(&icon, 1, 1, mps_file_in); /* unknown */ + fseek(mps_file_in, next_rec, SEEK_SET); + + wpt->shortname = xstrdup(wptname); + wpt->description = xstrdup(wptdesc); + wpt->position.latitude.degrees = lat / 2147483648.0 * 180.0; + wpt->position.longitude.degrees = lon / 2147483648.0 * 180.0; + wpt->icon_descr = mps_find_desc_from_icon_number(icon); + waypt_add(wpt); } -/* FIXME: cure the sign dependencies here. */ - fprintf(mapsource_file_out, "Waypoint %s %s ", waypointp->shortname, tbuf); - fprintf(mapsource_file_out, "N%02.0f", waypointp->position.latitude.degrees); - d = waypointp->position.latitude.degrees; - fprintf(mapsource_file_out, " %06.3f ", (waypointp->position.latitude.degrees - d) * 100); - fprintf(mapsource_file_out, "W%02.0f", fabs(waypointp->position.longitude.degrees)); - d = fabs(waypointp->position.longitude.degrees); - fprintf(mapsource_file_out, " %06.3f ", (fabs(waypointp->position.longitude.degrees) - d) * 100); - fprintf(mapsource_file_out, "0 ft Symbol & Name Unknown\n"); + } -void -mapsource_write(void) +static void +mps_waypt_pr(const waypoint *wpt) { - waypt_disp_all(mapsource_waypt_pr); + int reclen = 87 + strlen(wpt->shortname) + strlen(wpt->description); + char zbuf[100]; + char ffbuf[100]; + char display = 1; + char icon; + int lat = wpt->position.latitude.degrees / 180.0 * 2147483648.0; + int lon = wpt->position.longitude.degrees / 180.0 * 2147483648.0; + + memset(zbuf, 0, sizeof(zbuf)); + memset(ffbuf, 0xff, sizeof(ffbuf)); + + icon = mps_find_icon_number_from_desc(wpt->icon_descr); + fwrite(&reclen, 4, 1, mps_file_out); + fwrite("W", 1, 1, mps_file_out); + fputs(wpt->shortname, mps_file_out); + fwrite(zbuf, 1, 1, mps_file_out); + fwrite(zbuf, 9, 1, mps_file_out); + fwrite(ffbuf, 12, 1, mps_file_out); + fwrite(zbuf, 2, 1, mps_file_out); + fwrite(ffbuf, 4, 1, mps_file_out); + fwrite(&lat, 4, 1, mps_file_out); + fwrite(&lon, 4, 1, mps_file_out); + fwrite(zbuf, 9, 1, mps_file_out); + fputs(wpt->description, mps_file_out); + fwrite(zbuf, 10, 1, mps_file_out); + fwrite(&display, 1, 1, mps_file_out); /* Show waypoint w/ name */ + fwrite(zbuf, 7, 1, mps_file_out); + fwrite(&icon, 1, 1, mps_file_out); + fwrite(zbuf, 23, 1, mps_file_out); +} + +void +mps_write(void) +{ + fwrite(mps_hdr, sizeof(mps_hdr), 1, mps_file_out); + waypt_disp_all(mps_waypt_pr); + fwrite(mps_ftr, sizeof(mps_ftr), 1, mps_file_out); } -ff_vecs_t mapsource_vecs = { - mapsource_rd_init, - mapsource_wr_init, - mapsource_rd_deinit, - mapsource_wr_deinit, - mapsource_read, - mapsource_write, +ff_vecs_t mps_vecs = { + mps_rd_init, + mps_wr_init, + mps_rd_deinit, + mps_wr_deinit, + mps_read, + mps_write, }; diff --git a/gpsbabel/testo b/gpsbabel/testo index ba1702963..e69225757 100755 --- a/gpsbabel/testo +++ b/gpsbabel/testo @@ -32,12 +32,6 @@ ${PNAME} -i geo -f geocaching.loc -o mapsend -F ${TMPDIR}/mm.mapsend ${PNAME} -i mapsend -f ${TMPDIR}/mm.mapsend -o gpsutil -F ${TMPDIR}/mm.gps diff ${TMPDIR}/mm.gps ${TMPDIR}/gu.wpt -# Garmin Mapsource -rm -f ${TMPDIR}/mm.mapsource ${TMPDIR}/ms.gps -#${PNAME} -i geo -f geocaching.loc -o mapsource -F ${TMPDIR}/mm.mapsource -#${PNAME} -i mapsource -f ${TMPDIR}/mm.mapsource -o gpsutil -F ${TMPDIR}/ms.gps -# diff ${TMPDIR}/ms.gps ${TMPDIR}/gu.wpt - # Magellan serial # TODO @@ -188,3 +182,16 @@ rm -f ${TMPDIR}/xcsv.geo ${TMPDIR}/xcsv.xcsv ${PNAME} -i geo -f geocaching.loc -o xcsv,style=${TMPDIR}/testo.style -F ${TMPDIR}/xcsv.geo ${PNAME} -i xcsv,style=${TMPDIR}/testo.style -f ${TMPDIR}/xcsv.geo -o xcsv,style=${TMPDIR}/testo.style -F ${TMPDIR}/xcsv.xcsv diff -u ${TMPDIR}/xcsv.geo ${TMPDIR}/xcsv.xcsv + +# Garmin Mapsource This is a binary format with some undocumented +# fields. This test is therefore intentionally vague. We read a file, +# convert it to GPX, then write a file as MPS, then read it back and +# write it as GPX and compare them. Since we're writing both GPX files +# ourselves from the same version, we're immune to changes in our own +# GPX output. + +rm -fr ${TMPDIR}/ms.gpx ${TMPDIR}/ms[12].gpx +${PNAME} -i mapsource -f reference/mapsource.mps -o gpx -F ${TMPDIR}/ms1.gpx +${PNAME} -i mapsource -f reference/mapsource.mps -o mapsource -F ${TMPDIR}/ms.mps +${PNAME} -i mapsource -f ${TMPDIR}/ms.mps -o gpx -F ${TMPDIR}/ms2.gpx +diff ${TMPDIR}/ms1.gpx ${TMPDIR}/ms2.gpx diff --git a/gpsbabel/vecs.c b/gpsbabel/vecs.c index 6afd9856b..b6e7ddc87 100644 --- a/gpsbabel/vecs.c +++ b/gpsbabel/vecs.c @@ -34,7 +34,7 @@ extern ff_vecs_t gpsman_vecs; extern ff_vecs_t gpx_vecs; extern ff_vecs_t mag_vecs; extern ff_vecs_t mapsend_vecs; -extern ff_vecs_t mapsource_vecs; +extern ff_vecs_t mps_vecs; extern ff_vecs_t gpsutil_vecs; extern ff_vecs_t tiger_vecs; extern ff_vecs_t pcx_vecs; @@ -93,13 +93,11 @@ vecs_t vec_list[] = { "Garmin PCX5", "pcx" }, -#if 0 { - &mapsource_vecs, + &mps_vecs, "mapsource", "Garmin Mapsource" }, -#endif { &gpsutil_vecs, "gpsutil", -- 2.30.2